package org.vnl.platform.impl.openjpa.orm;

import java.util.Collection;


import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Query;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.vnl.platform.orm.Command;
import org.vnl.platform.orm.CommandParameter;
import org.vnl.platform.orm.IConnectionManager;
import org.vnl.platform.orm.IPersistenceManager;
import org.vnl.platform.orm.PageResult;
import org.vnl.platform.orm.QueryCommand;
import org.vnl.platform.orm.TransactionErrCode;
import org.vnl.platform.orm.TransactionException;

/**
 * 利用JPA實作了PDK的ORM機制,負責把ORM的指令轉化成JPA的方式來存取Entity.
 * 
 * @author Vaughan
 *
 */
public class JPAPersistenceManger implements IPersistenceManager {

    private static final Logger logger = Logger.getLogger(JPAPersistenceManger.class);
    
    private static final ThreadLocal<EntityManager> currentTxn = new ThreadLocal<EntityManager>();
       
    private IConnectionManager cm;
    
    public JPAPersistenceManger() {
        
    }
    
    @Override
    public void begin() {
        logger.debug("BEGNIN TRANSACTION-----------------------");
        EntityManager em = currentTxn.get();
        try {
            if (em == null) {
                logger.debug("Create current thread Entity Manager----------------------");
                EntityManagerFactory emf = (EntityManagerFactory) cm.getCurrentSession();
                em = emf.createEntityManager();
                currentTxn.set(em);
            }
            if (!em.getTransaction().isActive()) {
                logger.debug("Begin entity manager Transaction---------------------------");
                em.getTransaction().begin();
            }
        } catch (Throwable e) {
            logger.error("Start Transaction Error", e);
            throw new TransactionException(TransactionErrCode.ERR_SQL_EXCEPTION, new Object[] {e.getMessage()}, e);
        }
    }

    @Override
    public void commit() {
        EntityManager em = currentTxn.get();
        if (em == null) {
            return;
        }
        if (isRollbackOnly()) {
            throw new TransactionException(TransactionErrCode.TRANSACTION_MUST_ROLLBACK);
        }
        try {
            logger.debug("Commit Transaction-------------------------------");
            em.getTransaction().commit();
        } catch (Throwable e) {
            logger.error("Commit Error-----------------------",e);
            throw new TransactionException(TransactionErrCode.ERR_SQL_EXCEPTION, new Object[] {e.getMessage()}, e);
        } finally {
            if (em.isOpen()) {
                try {
                    em.close();
                } catch (Throwable e) {
                    logger.error("Cannot colse JPA Entity Manager",e);
                }
            }
            try {
                EntityManagerFactory emf = (EntityManagerFactory) cm.getCurrentSession();
                emf.close();
            } catch (Throwable e) {
                logger.error("Cannot colse JPA Entity Manager",e);
            }
        }
    }

    @Override
    public void rollback() {
        EntityManager em = currentTxn.get();
        if (em == null) {
            return;
        }
        try {
            logger.debug("Rollback Transaction-------------------------------");
            em.getTransaction().rollback();
        } catch (Throwable e) {
            logger.error("Rollback Error:-----------------------",e);
            throw new TransactionException(TransactionErrCode.ERR_SQL_EXCEPTION, new Object[] {e.getMessage()}, e);
        } finally {
            if (em.isOpen()) {
                try {
                    em.close();
                } catch (Throwable e) {
                    logger.error("Cannot colse JPA Entity Manager",e);
                }
            }
            try {
                EntityManagerFactory emf = (EntityManagerFactory) cm.getCurrentSession();
                emf.close();
            } catch (Throwable e) {
                logger.error("Cannot colse JPA Entity Manager",e);
            }
        }
    }

    @Override
    public void save(Command obj) {
        EntityManager em = currentTxn.get();
        if (em == null) {
            this.begin();
            em = currentTxn.get();
        }
        Query query = em.createNamedQuery(obj.getName());
        int ii=0;
        for (CommandParameter cp : obj.getCondition()) {
            if (StringUtils.isBlank(cp.getName())) {
                query.setParameter(ii++, cp.getValue());
            } else {
                query.setParameter(cp.getName(), cp.getValue());
            }
        }
        query.executeUpdate();
    }

    @Override
    public void create(Command obj) {
        EntityManager em = currentTxn.get();
        if (em == null) {
            this.begin();
            em = currentTxn.get();
        }
        em.persist(obj.getEntity());
    }

    @Override
    public void update(Command obj) {
        EntityManager em = currentTxn.get();
        if (em == null) {
            logger.debug("Global Transaction not found");
            this.begin();
            em = currentTxn.get();
        }
        em.merge(obj.getEntity());
    }

    @Override
    public void delete(Command obj) {
        EntityManager em = currentTxn.get();
        if (em == null) {
            this.begin();
            em = currentTxn.get();
        }
        em.remove(obj.getEntity());
    }

    @Override
    public Collection<? extends Object> query(QueryCommand obj) {
        EntityManager em = currentTxn.get();
        if (em == null) {
            logger.debug("Global Transaction not begin");
            this.begin();
            em = currentTxn.get();
        }
        Query query = null;
        if (obj.getResultClass() == null) {
            if (StringUtils.isBlank(obj.getNativeSQL())) {
                query = em.createNamedQuery(obj.getName());
            } else {
                query = em.createQuery(obj.getNativeSQL());
            }
        } else {
            if (StringUtils.isBlank(obj.getNativeSQL())) {
                query = em.createNamedQuery(obj.getName(), obj.getResultClass());
            } else {
                query = em.createQuery(obj.getNativeSQL(), obj.getResultClass());
            }
        }
        int ii=1;
        for (CommandParameter cp : obj.getCondition()) {
            if (StringUtils.isBlank(cp.getName())) {
                query.setParameter(ii++, cp.getValue());
            } else {
                query.setParameter(cp.getName(), cp.getValue());
            }
        }
        @SuppressWarnings("unchecked")
        Collection<? extends Object> results = query.getResultList();
        return results;
    }

    @Override
    public Object getByKey(QueryCommand key) {
        EntityManager em = currentTxn.get();
        if (em == null) {
            this.begin();
            em = currentTxn.get();
        }
        Object result = em.find(key.getResultClass(), key.getKey());
        return result;
    }

    @Override
    public PageResult queryPage(int currentPage, int pageSize, QueryCommand obj) {
        EntityManager em = currentTxn.get();
        if (em == null) {
            this.begin();
            em = currentTxn.get();
        }
        Query query = obj.getResultClass() == null ? em.createNamedQuery(obj.getName()) : em.createNamedQuery(obj.getName(), obj.getResultClass());
        int ii = 0;
        for (CommandParameter cp : obj.getCondition()) {
            if (StringUtils.isBlank(cp.getName())) {
                query.setParameter(ii++, cp.getValue());
            } else {
                query.setParameter(cp.getName(), cp.getValue());
            }
        }
        @SuppressWarnings("unchecked")
        Collection<? extends Object> results = query.getResultList();
        int count = results.size();
        int total = count / pageSize;
        if (count % pageSize > 0) {
            total++;
        }
        @SuppressWarnings("unchecked")
        PageResult page = new PageResult(currentPage, pageSize, total, query.setFirstResult(currentPage * pageSize).setMaxResults(pageSize).getResultList());
        return page;
    }

    @Override
    public void setConnectionManager(IConnectionManager cm) {
        this.cm = cm;
    }

    @Override
    public boolean isRollbackOnly() {
        EntityManager em = currentTxn.get();
        if (em == null) {
            return false;
        }
        return em.getTransaction().getRollbackOnly();
    }

    @Override
    public void setRollbackOnly(boolean rollbackOnly) {
        EntityManager em = currentTxn.get();
        if (em != null) {
            em.getTransaction().setRollbackOnly();
        }
    }

}
